﻿// **********************************************
// SharePoint WebPart: UsersADBrowser
// Author: Khristoforov Yuri
// WWW   : http://usersadbrowser.codeplex.com, http://activex.net.ru
// **********************************************

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
using System.Web;
using System.Web.Hosting;
using System.Web.Security;
using System.Data;
using System.Xml;
using System.IO;

namespace UsersADBrowser.Helpers
{
    public static class UsersADBrowserHelper
    {
        public static string GetOUTreeDataFromAD(string strLdapPath, string[] arrExcludeNames, StringBuilder sbOUs)
        {
            using (HostingEnvironment.Impersonate())
            {
                DirectoryEntry dePoint = new DirectoryEntry(strLdapPath);
                DirectorySearcher searcher = new DirectorySearcher(dePoint);
                searcher.Filter = "(objectCategory=organizationalUnit)";
                searcher.PageSize = 500;
                searcher.Sort = new SortOption("name", SortDirection.Ascending);
                searcher.PropertiesToLoad.Add("name");
                searcher.SearchScope = SearchScope.OneLevel;

                foreach (SearchResult res in searcher.FindAll())
                {
                    string strName = GetADProperty("name", res);
                    if (arrExcludeNames != null && Array.IndexOf(arrExcludeNames, strName.ToLower()) != -1)
                        continue;

                    sbOUs.Append("<item title=\"");
                    sbOUs.Append(HttpUtility.HtmlEncode(strName) + "\">");
                    //sbOUs.Append("path=\"" + HttpUtility.HtmlEncode(res.Path) + "\">");
                    GetOUTreeDataFromAD(res.Path, arrExcludeNames, sbOUs);
                    sbOUs.Append("</item>");
                }
                dePoint.Close();
                dePoint.Dispose();
            }

            return sbOUs.ToString();
        }

        public static string GetDefaultNamingContext()
        {
            string strNamingContext = "";
            using (HostingEnvironment.Impersonate())
            {
                using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE"))
                {
                    strNamingContext = "LDAP://" + rootDSE.Properties["defaultNamingContext"].Value.ToString();
                }
            }
            return strNamingContext;
        }

        public static string GetLdapPathFromRequest(string paramPath, string entryPath, char sepPath)
        {
            if (paramPath == "")
                return entryPath;

            string strUnitPath = "";
            if (paramPath.IndexOf(sepPath) != -1)
            {
                string[] arrParts = paramPath.Split(sepPath);
                Array.Reverse(arrParts);
                for (int i = 0; i < arrParts.Length; i++)
                {
                    arrParts[i] = EscapeLdapPath(arrParts[i]);
                }
                strUnitPath = "OU=" + string.Join(",OU=", arrParts) + ",";
            }
            else
                strUnitPath = "OU=" + EscapeLdapPath(paramPath) + ",";

            strUnitPath = entryPath.Substring(0, entryPath.IndexOf("://") + 3) + strUnitPath + entryPath.Substring(entryPath.IndexOf("://") + 3);

            return strUnitPath;
        }

        public static DataTable GetUsersListFromAD(string ldapPath, string filter, string sortProp, string[] arrProps,
                                                    bool isSubTree, bool isShowPhoto, bool isShowPresence, string[] arrExcludeNames)
        {
            DataTable dtUsersList = new DataTable("UsersList");
            using (HostingEnvironment.Impersonate())
            {
                DirectoryEntry dePoint = new DirectoryEntry(ldapPath);
                string strPointDistName = dePoint.Properties["distinguishedName"][0].ToString();
                DirectorySearcher searcher = new DirectorySearcher(dePoint);
                searcher.Filter = filter;
                searcher.PageSize = 500;
                searcher.Sort = new SortOption(sortProp, SortDirection.Ascending);
                searcher.SearchScope = isSubTree ? SearchScope.Subtree : SearchScope.OneLevel;

                dtUsersList.Columns.Add("UserAccount", System.Type.GetType("System.String"));
                if (isShowPhoto)
                    dtUsersList.Columns.Add("UserPhoto", System.Type.GetType("System.String"));

                if (isShowPresence)
                    dtUsersList.Columns.Add("UserPresence", System.Type.GetType("System.String"));

                for (int i = 0; i < arrProps.Length; i++)
                {
                    dtUsersList.Columns.Add(arrProps[i], System.Type.GetType("System.String"));
                    searcher.PropertiesToLoad.Add(arrProps[i]);
                }
                searcher.PropertiesToLoad.Add("userPrincipalName");
                searcher.PropertiesToLoad.Add("distinguishedName");
                searcher.PropertiesToLoad.Add("sAMAccountName");
                searcher.PropertiesToLoad.Add("msRTCSIP-PrimaryUserAddress");
                searcher.PropertiesToLoad.Add("mail");

                string strFqdn = GetFqdnFromEntry(strPointDistName);

                foreach (SearchResult res in searcher.FindAll())
                {
                    string strDistName = GetADProperty("distinguishedName", res);
                    List<string> lstOUs = GetOUsFromDistName(strDistName);

                    IEnumerable<string> arrInterSect = lstOUs.ToArray().Intersect(arrExcludeNames);

                    if (arrInterSect != null && arrInterSect.Count() > 0)
                        continue;

                    int j = isShowPhoto ? 2 : 1;
                    if (isShowPresence)
                        j = j + 1;

                    object[] arrPropValues = new object[arrProps.Length + j];
                    //string strUPN = GetADProperty("userPrincipalName", res);
                    string strAccountName = GetADProperty("sAMAccountName", res);
                    string strSipAddress = GetADProperty("msRTCSIP-PrimaryUserAddress", res);
                    string strEmailAddress = GetADProperty("mail", res);
                    arrPropValues[0] = strAccountName + "@" + strFqdn;

                    if (isShowPhoto)
                        arrPropValues[1] = "";

                    if (isShowPresence)
                        arrPropValues[j - 1] = strSipAddress + ";" + strEmailAddress;

                    for (int i = 0; i < arrProps.Length; i++)
                    {
                        arrPropValues[i + j] = nl2br(HttpUtility.HtmlEncode(GetADProperty(arrProps[i], res)));
                    }
                    dtUsersList.Rows.Add(arrPropValues);
                }

                dePoint.Close();
                dePoint.Dispose();
            }

            return dtUsersList;
        }

        public static DataTable SearchUsersList(DataTable dtUsersList, string strSearchQuery, string sortProp, string sortDir)
        {
            DataTable dtSearch = dtUsersList.Clone();
            foreach (DataRow row in dtUsersList.Rows)
                dtSearch.ImportRow(row);

            List<string> lstWhereClause = new List<string>();

            int i = 1;
            foreach (DataColumn col in dtSearch.Columns)
            {
                if (i > 1)
                {
                    lstWhereClause.Add(col.ColumnName + " LIKE '%" + HttpUtility.HtmlEncode(strSearchQuery).Replace("'", "''") + "%'");
                }
                i++;
            }

            if (strSearchQuery != "")
                dtSearch.DefaultView.RowFilter = String.Join(" OR ", lstWhereClause.ToArray());

            // !!!
            try
            {
                dtSearch.DefaultView.Sort = sortDir != "" ? sortDir : sortProp;
            }
            catch { }
            // !!!
            return dtSearch;
        }

        public static string[] Values2Lower(string[] arrData)
        {
            for (int i = 0; i < arrData.Length; i++)
            {
                arrData[i] = arrData[i].Trim().ToLower();
            }
            return arrData;
        }

        public static DataTable GetUserFromAD(string accountName, string[] arrProps, bool isShowPhoto, bool isShowPresence)
        {
            DataTable dtUserInfo = new DataTable("UserInfo");
            using (HostingEnvironment.Impersonate())
            {
                string[] arrLdapPaths = accountName.Split('@');
                DirectoryEntry dePoint = new DirectoryEntry("LDAP://" + arrLdapPaths[1]);
                DirectorySearcher searcher = new DirectorySearcher(dePoint);
                searcher.Filter = "sAMAccountName=" + EscapeLdapFilter(arrLdapPaths[0]);
                //searcher.Filter = "userPrincipalName=" + EscapeLdapFilter(accountName);
                searcher.SearchScope = SearchScope.Subtree;

                if (isShowPhoto)
                    dtUserInfo.Columns.Add("UserPhoto", System.Type.GetType("System.String"));

                for (int i = 0; i < arrProps.Length; i++)
                {
                    dtUserInfo.Columns.Add(arrProps[i], System.Type.GetType("System.String"));
                    searcher.PropertiesToLoad.Add(arrProps[i]);
                }

                if (isShowPresence)
                {
                    dtUserInfo.Columns.Add("UserPresence", System.Type.GetType("System.String"));
                    searcher.PropertiesToLoad.Add("msRTCSIP-PrimaryUserAddress");
                    searcher.PropertiesToLoad.Add("mail");
                }

                SearchResult res = searcher.FindOne();

                if (res != null)
                {
                    int j = isShowPhoto ? 1 : 0;
                    int k = isShowPresence ? 1 : 0;
                    object[] arrPropValues = new object[arrProps.Length + j + k];
                    if (isShowPhoto)
                        arrPropValues[0] = "";

                    for (int i = 0; i < arrProps.Length; i++)
                    {
                        arrPropValues[i + j] = nl2br(HttpUtility.HtmlEncode(GetADProperty(arrProps[i], res)));
                    }

                    if (isShowPresence)
                    {
                        arrPropValues[arrPropValues.Length - 1] = GetSipAddressFromString(GetADProperty("msRTCSIP-PrimaryUserAddress", res)) + ";" + GetADProperty("mail", res);
                    }

                    dtUserInfo.Rows.Add(arrPropValues);
                }

                dePoint.Close();
                dePoint.Dispose();
            }

            return dtUserInfo;
        }

        public static string GetMD5(string val)
        {
            return FormsAuthentication.HashPasswordForStoringInConfigFile(val, "md5");
        }

        public static string FormatLdapEntry(string entryPath)
        {
            string strRet = "";
            try
            {
                using (HostingEnvironment.Impersonate())
                {
                    using (DirectoryEntry de = new DirectoryEntry(entryPath))
                    {
                        strRet = "LDAP://" + de.Properties["distinguishedName"][0].ToString();
                    }
                }
            }
            catch
            {
                strRet = entryPath;
            }

            return strRet;
        }

        public static string GetFqdnFromEntry(string entryPath)
        {
            return (entryPath.ToUpper().Substring(entryPath.ToUpper().IndexOf("DC=") + 3).Replace(",DC=", ".").ToLower());
        }

        public static string GetSipAddressFromString(string val)
        {
            if (val.ToLower().StartsWith("sip:"))
            {
                return val.Substring(4);
            }

            return val;
        }

        private static string nl2br(string val)
        {
            return val.Replace("\r\n", "\r\n<br />");
        }

        private static string GetADProperty(string property, SearchResult res)
        {
            if (res.Properties.Contains(property))
            {
                ResultPropertyValueCollection col = res.Properties[property];
                if (col.Count > 0)
                {
                    StringBuilder sbRet = new StringBuilder();
                    for (int i = 0; i < col.Count; i++)
                    {
                        if (i > 0)
                            sbRet.Append("\r\n");

                        sbRet.Append(col[i].ToString());
                    }

                    return sbRet.ToString();
                }
            }

            return "";
        }

        private static List<string> GetOUsFromDistName(string strDistName)
        {
            List<string> lstOUs = new List<string>();
            string[] arrParts = strDistName.Split(',');
            for (int i = 0; i < arrParts.Length; i++)
            {
                if (arrParts[i].Trim().StartsWith("OU="))
                {
                    lstOUs.Add(arrParts[i].Trim().Replace("OU=", "").ToLower());
                }
            }

            return lstOUs;
        }

        private static string EscapeLdapPath(string val)
        {
            StringBuilder sbEscape = new StringBuilder();
            for (int i = 0; i < val.Length; ++i)
            {
                char current = val[i];
                switch (current)
                {
                    case ',':
                        sbEscape.Append(@"\,");
                        break;
                    case '\\':
                        sbEscape.Append(@"\\");
                        break;
                    case '#':
                        sbEscape.Append(@"\#");
                        break;
                    case '+':
                        sbEscape.Append(@"\+");
                        break;
                    case '<':
                        sbEscape.Append(@"\<");
                        break;
                    case '>':
                        sbEscape.Append(@"\>");
                        break;
                    case ';':
                        sbEscape.Append(@"\;");
                        break;
                    case '"':
                        sbEscape.Append("\\\"");
                        break;
                    case '=':
                        sbEscape.Append(@"\=");
                        break;
                    case '/':
                        sbEscape.Append(@"\/");
                        break;
                    default:
                        sbEscape.Append(current);
                        break;
                }
            }

            return sbEscape.ToString();
        }

        private static string EscapeLdapFilter(string val)
        {
            StringBuilder sbEscape = new StringBuilder();
            for (int i = 0; i < val.Length; ++i)
            {
                char current = val[i];
                switch (current)
                {
                    case '\\':
                        sbEscape.Append(@"\5c");
                        break;
                    case '*':
                        sbEscape.Append(@"\2a");
                        break;
                    case '(':
                        sbEscape.Append(@"\28");
                        break;
                    case ')':
                        sbEscape.Append(@"\29");
                        break;
                    case '\u0000':
                        sbEscape.Append(@"\00");
                        break;
                    case '/':
                        sbEscape.Append(@"\2f");
                        break;
                    default:
                        sbEscape.Append(current);
                        break;
                }
            }
            return sbEscape.ToString();
        }

    }
}
